package org.apache.ocean.server;


import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sourceforge.jsorter.ReflectColumns;
import net.sourceforge.jsorter.SortReflect;
import net.sourceforge.jsorter.SortableColumn;
import net.sourceforge.jsorter.Sorter;
import net.sourceforge.jsorter.SorterConstants;

import org.apache.commons.httpclient.HttpURL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.ocean.CLong;
import org.apache.ocean.ClusterConstants;
import org.apache.ocean.ClusterUtil;
import org.apache.ocean.SearchIndexShard;
import org.apache.ocean.SearchIndexShardManager;
import org.apache.ocean.SearchService;

import com.ibm.icu.text.DateFormat;

/**
 *
 * @author  jason rutherglen
 */
public class AdminServlet extends HttpServlet {
  public static Logger log = Logger.getLogger(AdminServlet.class.getName());
  public SearchService searchService;
  private static String CONTENT_TYPE = "text/html; charset=UTF-8";
  
  public AdminServlet() {
  }
  
  public void init() throws ServletException {
    try {
      searchService = SearchService.getSearchService();
      log.log(Level.INFO, "AdminServlet.init finished searchService: "+searchService);
    } catch (Throwable ex) {
      log.log(Level.SEVERE, "", ex);
    }
  }
  
  public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
      response.setContentType(CONTENT_TYPE);
      String action = request.getParameter("action");
      if (StringUtils.isBlank(action)) {
        handleMain(request, response);
      } else if (StringUtils.equals("search", action)) {
      	handleSearch(request, response);
      } else if (StringUtils.equals("showfile", action)) {
        handleShowFile(request, response);
      } else if (StringUtils.equals("displayFiles", action)) {
        handleDisplayFiles(request, response);
      } 
    } catch (Throwable ex) {
      log.log(Level.SEVERE, "", ex);
    }
  }
 
  public void sendError(Throwable throwable, HttpServletResponse response) throws IOException {
    String stackTrace = ExceptionUtils.getFullStackTrace(throwable);
    PrintWriter writer = response.getWriter();
    writer.print(stackTrace);
  }
  
  public class FileFilter implements IOFileFilter {
    public boolean accept(File file) {
      if (StringUtils.contains(file.getAbsolutePath(), "WEB-INF")) {
        return false;
      } else {
        return true;
      }
    }
    
    public boolean accept(java.io.File file, String str) {
      return false;
    }
  }
  
  public void handleSearch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  	String q = request.getParameter("q");
  	CLong start = ClusterUtil.getLong("start", request);
  	CLong rows = ClusterUtil.getLong("rows", request);
  	HttpURL searchURL = new HttpURL(request.getRequestURL().toString());
  	if (ClusterUtil.isNotBlank("xmlSearch", request)) {
  		searchURL.setPath("/ocean");
  		response.sendRedirect(searchURL.toString());
  	} else if (ClusterUtil.isNotBlank("htmlSearch", request)) {
  		searchURL.setPath("/ocean/admin");
  		handleHtmlSearch(request, response);
  	} else if (ClusterUtil.isNotBlank("termFrequency", request)) {
  		searchURL.setPath("/ocean");
  		response.sendRedirect(searchURL.toString());
  	} 
  }
  
  public void handleHtmlSearch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  	String queryString = request.getParameter("q");
  	CLong start = ClusterUtil.getLong("start", request);
  	CLong rows = ClusterUtil.getLong("rows", request);
  	
  }
  
  public void handleDisplayFiles(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String resinHome = System.getProperty("resin.home");
    File resinHomeDirectory = new File(resinHome);
    PrintWriter writer = response.getWriter();
    Collection logFiles = FileUtils.listFiles(resinHomeDirectory, new FileFilter(), TrueFileFilter.INSTANCE);
    
    ReflectColumns reflectColumns = new ReflectColumns();
    reflectColumns.add("getAbsolutePath", SortReflect.method("getAbsolutePath"), SorterConstants.ASCENDING_ORDER);
    
    response.setContentType(ClusterConstants.CONTENT_TYPE_HTML);
    
    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG);
    Sorter sorter = Sorter.createReflect(reflectColumns, new ArrayList(logFiles), SorterConstants.NULLS_ARE_LEAST);
    List sortedFiles = sorter.sortReflect();
    writer.println("<table border=0>");
    Iterator logFilesIterator = logFiles.iterator();
    while (logFilesIterator.hasNext()) {
      File logFile = (File)logFilesIterator.next();
      
      String filePath = logFile.getAbsolutePath();
      
      writer.println("<tr><td>");
      writer.println("<a href='/cluster/admin?action=showfile&file="+URLEncoder.encode(filePath)+"'>"+filePath+"</a>");
      writer.println("</td>");
      writer.println("<td>");
      Date lastModified = new Date(logFile.lastModified());
      String date = dateFormat.format(lastModified);
      writer.println(date);
      writer.println("</td>");
      String fileSizeStr = FileUtils.byteCountToDisplaySize(logFile.length());
      writer.println("<td>"+fileSizeStr+"</td>");
      writer.println("</tr>");
    }
    writer.println("</table>");
  }
  
  public void handleShowFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String fileParam = request.getParameter("file");
    File file = null;
    if (StringUtils.equals(fileParam, "searchconfig.xml")) {
      file = new File(System.getProperty("resin.home"), "searchconf/searchconfig.xml");
    } else {
      file = new File(fileParam);
    }
    if (file != null && file.exists()) {
      String contents = FileUtils.readFileToString(file, "UTF-8");
      response.setContentType("text/plain; charset=UTF-8");
      PrintWriter writer = response.getWriter();
      writer.write(contents);
    }
  }
  /**
   * public void handleMerge(HttpServletRequest request, HttpServletResponse response) throws Exception {
   * request.getParameterValues("directory");
   * String directoryString = request.getParameter("directory");
   * if (StringUtils.isBlank(directoryString)) return;
   * String[] array = StringUtils.split(directoryString, ":");
   * String fromDirectoryName = array[0];
   * String toDirectoryName = array[1];
   *
   * log.log(Level.INFO, "handleMerge fromDirectoryName: "+fromDirectoryName+" toDirectoryName: "+toDirectoryName);
   *
   * SearchDirectoryManager searchDirectoryManager = searchService.getSearchDirectoryManager();
   * SearchDirectory toSearchDirectory = searchDirectoryManager.getSearchDirectory(toDirectoryName);
   * SearchDirectory fromSearchDirectory = searchDirectoryManager.getSearchDirectory(fromDirectoryName);
   * //searchDirectoryManager.openNewDirectory(fromSearchDirectory);
   * //toSearchDirectory.merge(fromSearchDirectory);
   * }
   **/
  public String printSort(String label, String name, ReflectColumns reflectColumns, HttpServletRequest request) {
    String image = "sortup.gif";
    int order = SorterConstants.ASCENDING_ORDER;
    
    SortableColumn sortableColumn = reflectColumns.list.get(0);
    
    String currentOrder = request.getParameter("sort."+name);
    if (StringUtils.isBlank(currentOrder)) {
      if (name.equals(sortableColumn.getColumnName())) {
        currentOrder = "sort."+sortableColumn.getColumnOrder();
        order = sortableColumn.getColumnOrder();
      }
    } else if (StringUtils.equals("asc", currentOrder)) {
      order = SorterConstants.DESCENDING_ORDER;
    } else if (StringUtils.equals("desc", currentOrder)) {
      order = SorterConstants.ASCENDING_ORDER;
    }
    
    String orderString = null;
    
    if (order == SorterConstants.ASCENDING_ORDER) {
      image = "sortup.gif";
      orderString = "asc";
    } else if (order == SorterConstants.DESCENDING_ORDER) {
      image = "sortdown.gif";
      orderString = "desc";
    }
    
    String link = "/cluster/admin?sort."+name+"="+orderString;
    
    String html = "";
    
    html += "<a href='"+link+"'>";
    if (!StringUtils.isBlank(currentOrder)) html += "<b>";
    html += label;
    if (!StringUtils.isBlank(currentOrder)) html += "</b>";
    html += "</a>";
    html += "<a href='"+link+"'>";
    if (!StringUtils.isBlank(currentOrder)) {
      html += "<img src='/cluster/images/"+image+"' border=0>";
    }
    html += "</a>";
    
    return html;
  }
  
  public ReflectColumns getReflectColumns(HttpServletRequest request) {
    Enumeration enumeration = request.getParameterNames();
    String column = "getName";
    int order = SorterConstants.ASCENDING_ORDER;
    while (enumeration.hasMoreElements()) {
      String name = (String)enumeration.nextElement();
      
      if (name.startsWith("sort.")) {
        String[] array = StringUtils.split(name, ".");
        log.log(Level.INFO, "name array: "+Arrays.asList(array));
        column = array[1];
        String value = request.getParameter(name);
        
        if (value.equals("asc")) {
          order = SorterConstants.ASCENDING_ORDER;
        } else {
          order = SorterConstants.DESCENDING_ORDER;
        }
      }
    }
    ReflectColumns reflectColumns = new ReflectColumns();
    reflectColumns.add(column, SortReflect.method(column), order);
    return reflectColumns;
  }
  /**
  public void handleDisplayTransactions(HttpServletRequest request, HttpServletResponse response) throws Exception {
    PrintWriter writer = response.getWriter();
    writer.println("Transactions:<br>");
    String shardid = request.getParameter("shardid");
    SearchIndexShard searchIndexShard = searchService.getSearchIndexShard(shardid);
    int successes = searchIndexShard.getTransactionDatabase().findTransactionRecordCountByStatus(TransactionRecord.SUCCESS);
    int failures = searchIndexShard.getTransactionDatabase().findTransactionRecordCountByStatus(TransactionRecord.FAILURE);
    writer.println("Successes: "+successes+"<br>");
    writer.println("Failures: "+failures+"<br>");
    writer.println("<table border=0>");
    writer.println("<tr><td>ID</td><td>Type</td><td>Status</td><td>Doc ID</td><td>Version</td></tr>");
    //StoredSortedMap map = searchService.transactionDatabase.findTransactions();
    List<String> columnNames = new ArrayList<String>();
    columnNames.add("id");
    columnNames.add("status");
    columnNames.add("type");
    columnNames.add("documentID");
    columnNames.add("version");
    JDBCDatabase.Results results = null;
    try {
      SQLTransactionDatabase transactionDatabase = searchIndexShard.getTransactionDatabase();
      results = transactionDatabase.findTransactionRecordsResults(columnNames);
      List<String> cls = transactionDatabase.getColumnNames(results.resultSet);
      while (results.hasNext()) {
        /**
        NamedList row = transactionDatabase.toListRow(results.resultSet, cls, SQLTransactionDatabase.TRANSACTION_DEFAULTS);
        TransactionRecord transaction = transactionDatabase.toTransactionRecord(row);
        writer.println("<tr>");
        String link = "<a href='/cluster/select?action=gettransaction&id="+transaction.id+"'/>";
        writer.println("<td>"+link+transaction.id+"</a></td>");
        String statusString = "";
        if (ObjectUtils.equals(transaction.status, TransactionRecord.SUCCESS)) {
          statusString = "success";
        } else if (ObjectUtils.equals(transaction.status, TransactionRecord.FAILURE)) {
          statusString = "failure";
        }
        writer.println("<td>"+link+transaction.type+"</a></td>");
        writer.println("<td>"+link+statusString+"</a></td>");
        writer.println("<td>"+link+transaction.documentID+"</a></td>");
        writer.println("<td>"+link+transaction.version+"</a></td></tr>");
        //writer.println("<tr><td colspan=4>"+StringEscapeUtils.escapeHtml(transaction.xml)+"</td>");
        writer.println("</tr>");
        
      }
      writer.println("</table>");
    } finally {
      if (results != null) results.close();
    }
  }
  **/
  public void handleMain(HttpServletRequest request, HttpServletResponse response) throws Exception {
    PrintWriter writer = response.getWriter();
    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
    
    writer.println("<form action='"+request.getRequestURI()+"' method='GET'>");
    writer.println("<input type=hidden name=action value=search>");
    writer.println("Query: <textarea cols=100 rows=10 name='q'></textarea>");
    writer.println("Start: <input type=text size=4 name='start' value=''>");
    writer.println("Rows: <input type=text size=4 name='rows' value=''>");
    writer.println("<input type=submit name='xmlSearch' value='XML'>");
    writer.println("<input type=submit name='htmlSearch' value='HTML'>");
    writer.println("<input type=submit name='termFrequency' value='Term Frequency'>");
    writer.println("</form>");
    writer.println("<form action='select' method='GET'>");
    writer.println("<input type=hidden name='action' value='gettransaction'>");
    writer.println("Transaction ID: <input type=text size=10 name='id' value=''>");
    writer.println("<input type=submit name='get' value='Get'>");
    writer.println("</form>");
    
    writer.println("<table border=0>");
    /**
    NameCell nameCell = searchService.nameClient.getNameCell();
    writer.println("<tr><td>Name ID</td><td>Status</td><td>URL</td><td>Last Update</td></tr>");
    for (org.apache.solr.cluster.name.Name name : nameCell.getNames())  {
      writer.println("<tr>");
      writer.println("<td>"+name.id+"</td>");
      writer.println("<td>"+name.lastUpdate+"</td>");
      writer.println("<td>"+name.status+"</td>");
      writer.println("<td>"+name.url+"</td>");
      writer.println("</tr>");
    }
    writer.println("</table>");
    **/
    writer.print("<a href='/cluster/admin?action=displayTransactions'>Transactions</a> | ");
    writer.print("<a href='/cluster/admin?action=displayFiles'>Files</a> | ");
    writer.print("<a href='/cluster/admin?action=showfile&file=node.xml'>node.xml</a> | ");
    writer.print("<a href='/cluster/admin?action=showfile&file=cell.xml'>cell.xml</a> | ");
    writer.println("<a href='/cluster/admin?action=showfile&file=searchconfig.xml'>searchconfig.xml</a>");
    writer.println("<a href='/cluster/admin?action=resetclusterservice'>Reset</a><br>");
    
    writer.println("Status: "+searchService.getReportableStatus()+"<br>");
    ThreadPoolExecutor threadPool = searchService.getSearchThreadPool();
    
    writer.println("Thread pool size: "+threadPool.getPoolSize()+"<br>");
    writer.println("Thread active count: "+threadPool.getActiveCount()+"<br>");
    
    //Document lowestTransactionDocument = searchService.searchLowestTransactionDocument().document;
    //Document highestTransactionDocument = searchService.searchHighestTransactionDocument();
    //TransactionID lowestTransactionID = null;
    //if (lowestTransactionDocument != null) lowestTransactionID = ClusterUtil.getTransactionID(lowestTransactionDocument, searchService.getIndexSchema());
    writer.println("<form action='admin' method='POST'>");
    writer.println("Search Directories:<br>");
    writer.println("<table border=0>");
    
    ReflectColumns reflectColumns = getReflectColumns(request);
    
    writer.println("<tr><td>"+printSort("Name", "getName", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Type", "getType", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Docs", "numDocs", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Size", "getSize", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Files", "getFileCount", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Added", "getDocsAdded", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Deleted", "getDocsDeleted", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Status", "isClosed", reflectColumns, request)+"</td>");
    writer.println("<td>"+printSort("Date", "getDateCreated", reflectColumns, request)+"</td></tr>");
    
    SearchIndexShardManager searchDirectoryManager = searchService.getSearchIndexShardManager();
    //log.log(Level.INFO, "getSearchDirectoryManager");
    
    int totalNumDocs = 0;
    long totalSize = 0;
    long totalDocsAdded = 0;
    int totalFileCount = 0;
    long totalDocsDeleted = 0;
    
    //List<SearchDirectory> readDirectories = searchDirectoryManager.getByType(SearchDirectory.READ);
    //log.log(Level.INFO, "searchDirectoryManager.getByType");
    List<SearchIndexShard> searchDirectories = searchDirectoryManager.getSearchIndexShards();
    //log.log(Level.INFO, "searchDirectoryManager.getSearchDirectories");
    
    Sorter sorter = Sorter.createReflect(reflectColumns, searchDirectories, SorterConstants.NULLS_ARE_LEAST);
    List<SearchIndexShard> sortedSearchDirectories = sorter.sortReflect();
    
    for (SearchIndexShard indexShard : sortedSearchDirectories) {
      String name = indexShard.getName();
      String type = indexShard.getType();
      CLong size = indexShard.getSize();
      totalSize += size.value;
      CLong numDocs = indexShard.numDocs();
      totalNumDocs += numDocs.value;
      Date dateCreated = indexShard.getDateCreated();
      String dateCreatedString = dateFormat.format(dateCreated);
      String sizeStr = FileUtils.byteCountToDisplaySize((int)size.value);
      int fileCount = indexShard.getFileCount();
      String status = "";
      if (indexShard.isClosed()) {
        status = "closed";
      }
      writer.println("<tr><td>"+name+"</td><td>"+type+"</td><td>"+numDocs+"</td>");
      writer.println("<td>"+sizeStr+"</td><td>"+fileCount+"</td>");
      writer.println("<td>"+status+"</td>");
      writer.println("<td>"+dateCreatedString+"</td>");
      writer.println("</tr>");
      /**
       * if (type.equals(SearchDirectory.READ_WRITE)) {
       * writer.println("<td>");
       * writer.println("<select name='directory'>");
       * writer.println("<option selected='selected' value=''>None</option>");
       * for (SearchDirectory readDirectory : readDirectories) {
       * writer.println("<option value='"+name+":"+readDirectory.getName()+"'>"+readDirectory.getName()+"</option>");
       * }
       * writer.println("</select>");
       * writer.println("</td></tr>");
       * } else {
       * writer.println("<td>&nbsp;</td></tr>");
       * }
       **/
    }
    writer.println("<tr><td>&nbsp;</td><td>&nbsp;</td><td>"+totalNumDocs+"</td><td>"+FileUtils.byteCountToDisplaySize(totalSize)+"</td>");
    writer.println("<td>"+totalFileCount+"</td><td>"+totalDocsAdded+"</td></tr>");
    //writer.println("<tr><td align=right colspan=6>");
    //writer.println("<input type=submit name='merge' value='Merge'>");
    //writer.println("</td></tr>");
    writer.println("</table>");
    writer.println("</form>");
    /**
     *
     * ReplicationServer replicationServer = transactionDatabase.getReplicationServer();
     * if (replicationServer != null) {
     * writer.println("Replication Server: <br>");
     * writer.println("<table border=0>");
     * writer.println("<tr><td>Host</td><td>Last Keep Alive</td></tr>");
     * List<ReplicationServer.Connection> connections = replicationServer.getConnections();
     * for (ReplicationServer.Connection connection : connections) {
     * if (connection.socket != null) {
     * SocketAddress socketAddress = connection.socket.getRemoteSocketAddress();
     * //String status = connection.lastKeepAliveSent.toString();
     * String keepAliveString = "";
     * if (connection.lastKeepAliveSent != null) {
     * long durationMillis = System.currentTimeMillis() - connection.lastKeepAliveSent.getTime();
     * keepAliveString = DurationFormatUtils.formatDurationHMS(durationMillis);
     * }
     * writer.println("<tr><td>"+socketAddress+"</td><td>"+keepAliveString+"</td></tr>");
     * } else {
     * writer.println("<tr><td>No socket</td><td></td></tr>");
     * }
     * }
     * writer.println("</table>");
     * }
     *
     * ReplicationClient replicationClient = transactionDatabase.getReplicationClient();
     * if (replicationClient != null) {
     * writer.println("Replication Client: <br>");
     * writer.println("<table border=0>");
     * writer.println("<tr><td>Host</td><td>Last Keep Alive</td></tr>");
     * SocketAddress remoteSocketAddress = replicationClient.socket.getRemoteSocketAddress();
     * String keepAliveString = "";
     * if (replicationClient.lastKeepAliveSent != null) {
     * long durationMillis = System.currentTimeMillis() - replicationClient.lastKeepAliveSent.getTime();
     * keepAliveString = DurationFormatUtils.formatDurationHMS(durationMillis);
     * }
     * writer.println("<tr><td>"+remoteSocketAddress+"</td><td>"+keepAliveString+"</td></tr>");
     * writer.println("</table>");
     * }
     **/
    /**
    ConcurrentMultiSearcher concurrentMultiSearcher = searchService.getConcurrentMultiSearcher();
    writer.println("Searchables: <br>");
    writer.println("<table border=0>");
    Searchable[] searchables = concurrentMultiSearcher.getSearchables();
    for (int x=0; x < searchables.length; x++) {
      ClusterIndexSearcher indexSearcher = (ClusterIndexSearcher)searchables[x];
      String name = indexSearcher.searchIndexShard.getName();
      writer.println("<tr><td>"+name+"</td></tr>");
    }
    writer.println("</table>");
    **/
    /**
    TransactionRecord lastTransaction = searchService.transactionDatabase.findLastTransactionRecord();
    if (lastTransaction != null) {
      writer.println("Last Transaction:<br>");
      writer.println("<table border=0>");
      writer.println("<tr><td>ID</td><td>"+lastTransaction.id+"</td></tr>");
      writer.println("<tr><td>Version</td><td>"+lastTransaction.version+"</td></tr>");
      writer.println("<tr><td>Type</td><td>"+lastTransaction.type+"</td></tr>");
      writer.println("<tr><td>Date Created</td><td>"+lastTransaction.dateCreated+"</td></tr>");
      writer.println("<tr><td>XML</td><td>"+org.apache.solr.cluster.util.HTMLEncoder.encode(lastTransaction.toXML())+"</td></tr>");
      writer.println("</table>");
    }
    **/
  }
}
